﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Text;
using ParserGenerator.CharacterMapping;
using ParserGenerator.Collections;
using ParserGenerator.Exceptions;
using ParserGenerator.Extensions;
using ParserGenerator.Input;
using ParserGenerator.Output;

namespace ParserGenerator.Languages.C
{
    partial class TypeName
    {
        #region predefined types
        public static TypeSpecifier VoidSpecifier { get; } = TypeSpecifier.NewVoid();
        public static TypeSpecifier CharSpecifier { get; } = TypeSpecifier.NewChar();
        public static TypeSpecifier ShortSpecifier { get; } = TypeSpecifier.NewShort();
        public static TypeSpecifier IntSpecifier { get; } = TypeSpecifier.NewInt();
        public static TypeSpecifier LongSpecifier { get; } = TypeSpecifier.NewLong();
        public static TypeSpecifier Char8TSpecifier { get; } = TypeSpecifier.NewChar8T();
        public static TypeSpecifier Char16TSpecifier { get; } = TypeSpecifier.NewChar16T();
        public static TypeSpecifier Char32TSpecifier { get; } = TypeSpecifier.NewChar32T();
        public static TypeSpecifier WCharTSpecifier { get; } = TypeSpecifier.NewWCharT();
        public static TypeSpecifier IntPtrTSpecifier { get; } = TypeSpecifier.NewIntPtrT();
        public static TypeSpecifier FloatSpecifier { get; } = TypeSpecifier.NewFloat();
        public static TypeSpecifier DoubleSpecifier { get; } = TypeSpecifier.NewDouble();
        public static TypeSpecifier SignedSpecifier { get; } = TypeSpecifier.NewSigned();
        public static TypeSpecifier UnsignedSpecifier { get; } = TypeSpecifier.NewUnsigned();
        public static TypeSpecifier BoolSpecifier { get; } = TypeSpecifier.NewBool();
        public static TypeSpecifier ComplexSpecifier { get; } = TypeSpecifier.NewComplex();

        public static TypeQualifier ConstQualifier { get; } = TypeQualifier.NewConst();
        public static TypeQualifier RestrictQualifier { get; } = TypeQualifier.NewRestrict();
        public static TypeQualifier VolatileQualifier { get; } = TypeQualifier.NewVolatile();
        public static TypeQualifier AtomicQualifier { get; } = TypeQualifier.NewAtomic();

        public static TypeName Void { get; } = Create(VoidSpecifier);
        public static TypeName Char { get; } = Create(CharSpecifier);
        public static TypeName Short { get; } = Create(ShortSpecifier);
        public static TypeName Int { get; } = Create(IntSpecifier);
        public static TypeName Long { get; } = Create(LongSpecifier);
        public static TypeName LongLong { get; } = Create(LongSpecifier, LongSpecifier);
        public static TypeName SChar { get; } = Create(SignedSpecifier, CharSpecifier);
        public static TypeName UChar { get; } = Create(UnsignedSpecifier, CharSpecifier);
        public static TypeName UShort { get; } = Create(UnsignedSpecifier, ShortSpecifier);
        public static TypeName UInt { get; } = Create(UnsignedSpecifier, IntSpecifier);
        public static TypeName ULong { get; } = Create(UnsignedSpecifier, LongSpecifier);
        public static TypeName ULongLong { get; } = Create(UnsignedSpecifier, LongSpecifier, LongSpecifier);
        public static TypeName Char8T { get; } = Create(Char8TSpecifier);
        public static TypeName Char16T { get; } = Create(Char16TSpecifier);
        public static TypeName Char32T { get; } = Create(Char32TSpecifier);
        public static TypeName WCharT { get; } = Create(WCharTSpecifier);
        public static TypeName IntPtrT { get; } = Create(IntPtrTSpecifier);
        public static TypeName UIntPtrT { get; } = Create(UnsignedSpecifier, IntPtrTSpecifier);
        public static TypeName Float { get; } = Create(FloatSpecifier);
        public static TypeName Double { get; } = Create(DoubleSpecifier);
        public static TypeName LongDouble { get; } = Create(LongSpecifier, DoubleSpecifier);
        public static TypeName Bool { get; } = Create(BoolSpecifier);
        public static TypeName FloatComplex { get; } = Create(FloatSpecifier, ComplexSpecifier);
        public static TypeName DoubleComplex { get; } = Create(DoubleSpecifier, ComplexSpecifier);
        public static TypeName LongDoubleComplex { get; } = Create(LongSpecifier, DoubleSpecifier, ComplexSpecifier);

        public static TypeName VoidP { get; } = CreatePointer(VoidSpecifier);
        public static TypeName CharP { get; } = CreatePointer(CharSpecifier);
        public static TypeName SCharP { get; } = CreatePointer(SignedSpecifier, CharSpecifier);
        public static TypeName UCharP { get; } = CreatePointer(UnsignedSpecifier, CharSpecifier);
        public static TypeName Char8TP { get; } = CreatePointer(Char8TSpecifier);
        public static TypeName Char16TP { get; } = CreatePointer(Char16TSpecifier);
        public static TypeName Char32TP { get; } = CreatePointer(Char32TSpecifier);
        public static TypeName WCharTP { get; } = CreatePointer(WCharTSpecifier);
        public static TypeName ConstVoidP { get; } = CreatePointer(ConstQualifier, VoidSpecifier);
        public static TypeName ConstCharP { get; } = CreatePointer(ConstQualifier, CharSpecifier);
        public static TypeName ConstSCharP { get; } = CreatePointer(ConstQualifier, SignedSpecifier, CharSpecifier);
        public static TypeName ConstUCharP { get; } = CreatePointer(ConstQualifier, UnsignedSpecifier, CharSpecifier);
        public static TypeName ConstChar8TP { get; } = CreatePointer(ConstQualifier, Char8TSpecifier);
        public static TypeName ConstChar16TP { get; } = CreatePointer(ConstQualifier, Char16TSpecifier);
        public static TypeName ConstChar32TP { get; } = CreatePointer(ConstQualifier, Char32TSpecifier);
        public static TypeName ConstWCharTP { get; } = CreatePointer(ConstQualifier, WCharTSpecifier);

        public static TypeName Create(TypeSpecifier t)
        {
            if (t == null)
                throw new ArgumentNullException(nameof(t));
            var item = SpecifierQualifierListItem.NewTypeSpecifier(t);
            var list = SpecifierQualifierList.New(Sequence.Create(item));
            return New(list);
        }

        public static TypeName Create(TypeSpecifier t1, TypeSpecifier t2)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2));
            return New(list);
        }

        public static TypeName Create(TypeSpecifier t1, TypeSpecifier t2, TypeSpecifier t3)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (t3 == null)
                throw new ArgumentNullException(nameof(t3));
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var item3 = SpecifierQualifierListItem.NewTypeSpecifier(t3);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2, item3));
            return New(list);
        }

        public static TypeName CreatePointer(TypeSpecifier t)
        {
            if (t == null)
                throw new ArgumentNullException(nameof(t));
            var item = SpecifierQualifierListItem.NewTypeSpecifier(t);
            var list = SpecifierQualifierList.New(Sequence.Create(item));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreatePointer(TypeSpecifier t1, TypeSpecifier t2)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreatePointer(TypeSpecifier t1, TypeSpecifier t2, TypeSpecifier t3)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (t3 == null)
                throw new ArgumentNullException(nameof(t3));
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var item3 = SpecifierQualifierListItem.NewTypeSpecifier(t3);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2, item3));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreatePointer(TypeQualifier q, TypeSpecifier t)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t == null)
                throw new ArgumentNullException(nameof(t));
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item = SpecifierQualifierListItem.NewTypeSpecifier(t);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreatePointer(TypeQualifier q, TypeSpecifier t1, TypeSpecifier t2)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item1, item2));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreatePointer(TypeQualifier q, TypeSpecifier t1, TypeSpecifier t2, TypeSpecifier t3)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (t3 == null)
                throw new ArgumentNullException(nameof(t3));
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var item3 = SpecifierQualifierListItem.NewTypeSpecifier(t3);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item1, item2, item3));
            var pointer = PointerDeclarator.New(Sequence.Create(PointerDeclaratorItem.New()));
            var declarator = AbstractDeclarator.NewPointer(pointer);
            return New(list, declarator);
        }

        public static TypeName CreateArray(TypeSpecifier t, int size)
        {
            if (t == null)
                throw new ArgumentNullException(nameof(t));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var item = SpecifierQualifierListItem.NewTypeSpecifier(t);
            var list = SpecifierQualifierList.New(Sequence.Create(item));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }

        public static TypeName CreateArray(TypeSpecifier t1, TypeSpecifier t2, int size)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }

        public static TypeName CreateArray(TypeSpecifier t1, TypeSpecifier t2, TypeSpecifier t3, int size)
        {
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (t3 == null)
                throw new ArgumentNullException(nameof(t3));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var item3 = SpecifierQualifierListItem.NewTypeSpecifier(t3);
            var list = SpecifierQualifierList.New(Sequence.Create(item1, item2, item3));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }

        public static TypeName CreateArray(TypeQualifier q, TypeSpecifier t, int size)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t == null)
                throw new ArgumentNullException(nameof(t));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item = SpecifierQualifierListItem.NewTypeSpecifier(t);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }

        public static TypeName CreateArray(TypeQualifier q, TypeSpecifier t1, TypeSpecifier t2, int size)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item1, item2));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }

        public static TypeName CreateArray(TypeQualifier q, TypeSpecifier t1, TypeSpecifier t2, TypeSpecifier t3, int size)
        {
            if (q == null)
                throw new ArgumentNullException(nameof(q));
            if (t1 == null)
                throw new ArgumentNullException(nameof(t1));
            if (t2 == null)
                throw new ArgumentNullException(nameof(t2));
            if (t3 == null)
                throw new ArgumentNullException(nameof(t3));
            if (size <= 0)
                throw new ArgumentOutOfRangeException(nameof(size), SR.SizeMustBePositive);
            var qual = SpecifierQualifierListItem.NewTypeQualifier(q);
            var item1 = SpecifierQualifierListItem.NewTypeSpecifier(t1);
            var item2 = SpecifierQualifierListItem.NewTypeSpecifier(t2);
            var item3 = SpecifierQualifierListItem.NewTypeSpecifier(t3);
            var list = SpecifierQualifierList.New(Sequence.Create(qual, item1, item2, item3));
            var num = Expression.NewIntegerConstant(IntegerConstant.New((uint)size, UIntPtrT));
            var direct = DirectAbstractDeclarator.NewArray(null, null, num);
            var array = AbstractDeclarator.NewPointerDirect(null, direct);
            return New(list, array);
        }
        #endregion
    }

    partial class CProgramParser
    {
        #region tokenizer semantic code
        private readonly StringBuilder _sb = new StringBuilder();
        private SourceContext _context;

        private void BeginString(int start)
        {
            _sb.Clear();
            _context = TokenContext;
            PushStart(start);
        }

        private void AppendTokenString()
        {
            string str = TokenContext.GetSource();
            _sb.Append(str);
        }

        private void AppendChar(char value)
        {
            _sb.Append(value);
        }

        private void AppendEscape(int n, bool isOctal)
        {
            string str = TokenContext.GetSource();
            if (n == -1)
                n = str.Length - (isOctal ? 1 : 2);
            BigInteger num = 0;
            if (isOctal)
            {
                for (int i = 0; i < n; i++)
                {
                    int ch = str[1 + i];
                    int d = ch.ValueOfDigit();
                    num = (num << 3) | d;
                }
            }
            else
            {
                for (int i = 0; i < n; i++)
                {
                    int ch = str[2 + i];
                    int d = ch.ValueOfDigit();
                    num = (num << 4) | d;
                }
            }
            if (num < CharExtensions.MinChar || num > CharExtensions.MaxChar)
            {
                string message = string.Format(SR.CharMustBeInRange, CharInterval.Universal);
                throw SyntaxError(message);
            }
            int v = (int)num;
            _sb.Append(char.ConvertFromUtf32(v));
        }

        private void EndString(bool asIdentifier)
        {
            string value = _sb.ToString();
            if (asIdentifier)
            {
                TokenContext = new SourceContext(_context.Begin, TokenContext.Begin);
                TokenValue = Identifier.New(value, TokenContext);
            }
            else
            {
                TokenContext = new SourceContext(_context.Begin, TokenContext.End);
                string str = TokenContext.GetSource();
                if (str.StartsWith("u8"))
                {
                    List<byte> list = new List<byte>();
                    foreach (int c in value.EnumCodepoints())
                    {
                        string chars = char.ConvertFromUtf32(c);
                        foreach (byte ch in Encoding.UTF8.GetBytes(chars))
                            list.Add(ch);
                    }
                    list.Add(0);
                    ReadOnlyCollection<byte> val = list.ToArray().AsReadOnly();
                    TypeName type = TypeName.CreateArray(TypeName.ConstQualifier, TypeName.Char8TSpecifier, val.Count);
                    TokenValue = StringLiteral.NewChar8(val, type, TokenContext);
                }
                else if (str.StartsWith("u"))
                {
                    List<char> list = new List<char>();
                    foreach (int c in value.EnumCodepoints())
                    {
                        string chars = char.ConvertFromUtf32(c);
                        foreach (char ch in chars)
                            list.Add(ch);
                    }
                    list.Add('\0');
                    ReadOnlyCollection<char> val = list.ToArray().AsReadOnly();
                    TypeName type = TypeName.CreateArray(TypeName.ConstQualifier, TypeName.Char16TSpecifier, val.Count);
                    TokenValue = StringLiteral.NewChar16(val, type, TokenContext);
                }
                else if (str.StartsWith("U"))
                {
                    List<int> list = new List<int>();
                    foreach (int c in value.EnumCodepoints())
                        list.Add(c);
                    list.Add(0);
                    ReadOnlyCollection<int> val = list.ToArray().AsReadOnly();
                    TypeName type = TypeName.CreateArray(TypeName.ConstQualifier, TypeName.Char32TSpecifier, val.Count);
                    TokenValue = StringLiteral.NewChar32(val, type, TokenContext);
                }
                else if (str.StartsWith("L"))
                {
                    List<char> list = new List<char>();
                    foreach (int c in value.EnumCodepoints())
                    {
                        string chars = char.ConvertFromUtf32(c);
                        foreach (char ch in chars)
                            list.Add(ch);
                    }
                    list.Add('\0');
                    ReadOnlyCollection<char> val = list.ToArray().AsReadOnly();
                    TypeName type = TypeName.CreateArray(TypeName.ConstQualifier, TypeName.WCharTSpecifier, val.Count);
                    TokenValue = StringLiteral.NewWChar(val, type, TokenContext);
                }
                else
                {
                    List<byte> list = new List<byte>();
                    foreach (int c in value.EnumCodepoints())
                    {
                        if (0 <= c && c <= 0xFF)
                        {
                            list.Add((byte)c);
                            continue;
                        }
                        string chars = char.ConvertFromUtf32(c);
                        foreach (byte ch in Encoding.UTF8.GetBytes(chars))
                            list.Add(ch);
                    }
                    list.Add(0);
                    ReadOnlyCollection<byte> val = list.ToArray().AsReadOnly();
                    TypeName type = TypeName.CreateArray(TypeName.ConstQualifier, TypeName.CharSpecifier, val.Count);
                    TokenValue = StringLiteral.NewChar(val, type, TokenContext);
                }
            }
            PopStart();
        }

        private void EndChar()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            string value = _sb.ToString();
            int[] codepoints = value.GetCodepoints();
            if (codepoints.Length == 0)
                throw SyntaxError(SR.CharacterConstantEmpty, TokenContext);
            else if (codepoints.Length > 1)
                throw SyntaxError(SR.CharacterConstantManyChars, TokenContext);
            int c = codepoints[0];
            string str = TokenContext.GetSource();
            if (str.StartsWith("U"))
            {
                if (c < 0 || c > 0x10FFFF)
                    throw SyntaxError(SR.ValueMustBeInRangeForUChar, TokenContext);
                TypeName type = TypeName.Char32T;
                TokenValue = CharacterConstant.NewChar32(c, type, TokenContext);
            }
            else if (str.StartsWith("u"))
            {
                if (c < 0 || c > 0xFFFF)
                    throw SyntaxError(SR.ValueMustBeInRangeForuChar, TokenContext);
                TypeName type = TypeName.Char16T;
                TokenValue = CharacterConstant.NewChar16((char)c, type, TokenContext);
            }
            else if (str.StartsWith("L"))
            {
                if (c < 0 || c > 0xFFFF)
                    throw SyntaxError(SR.ValueMustBeInRangeForLChar, TokenContext);
                TypeName type = TypeName.WCharT;
                TokenValue = CharacterConstant.NewWChar((char)c, type, TokenContext);
            }
            else
            {
                if (c < 0 || c > 0xFF)
                    throw SyntaxError(SR.ValueMustBeInRangeForChar, TokenContext);
                TypeName type = TypeName.UChar;
                TokenValue = CharacterConstant.NewChar((byte)c, type, TokenContext);
            }
            PopStart();
        }

        private SyntaxErrorException CharacterConstantEmpty()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.CharacterConstantEmpty, TokenContext);
        }

        private SyntaxErrorException CharacterConstantManyChars()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.CharacterConstantManyChars, TokenContext);
        }

        private SyntaxErrorException UnterminatedCharacterConstant()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.UnterminatedCharacterConstant, TokenContext);
        }

        private SyntaxErrorException UnterminatedStringLiteral()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.UnterminatedStringLiteral, TokenContext);
        }

        private void ScanIntegerValue(int radix)
        {
            string str = TokenContext.GetSource();
            int start = (radix == 16) ? 2 : 0;
            BigInteger v = 0;
            int i;
            for (i = start; i < str.Length; i++)
            {
                int d = str[i].ValueOfDigit();
                if (d == -1)
                    break;
                v = v * radix + d;
            }
            if (v > ulong.MaxValue)
            {
                SyntaxErrorException e = new SyntaxErrorException(SR.IntegerConstantValueTooHugeToFitIntoUInt64);
                e.AddLocation(TokenContext);
                throw e;
            }
            ulong value = (ulong)v;
            string suffix = str.Substring(i).ToUpperInvariant();
            bool hasSuffix = suffix.Length > 0;
            bool isUnsigned = false;
            if (suffix.StartsWith("U"))
            {
                isUnsigned = true;
                suffix = suffix.Substring(1);
            }
            else if (suffix.EndsWith("U"))
            {
                isUnsigned = true;
                suffix = suffix.Substring(0, suffix.Length - 1);
            }
            TypeName type;
            if (radix == 10)
            {
                if (!hasSuffix)
                {
                    if (value <= int.MaxValue)
                        type = TypeName.Int;
                    else if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                    {
                        SyntaxErrorException e = new SyntaxErrorException(SR.IntegerConstantValueTooHugeToFitIntoInt64);
                        e.AddLocation(TokenContext);
                        throw e;
                    }
                }
                else if (isUnsigned && suffix.Length == 0)
                {
                    if (value <= uint.MaxValue)
                        type = TypeName.UInt;
                    else
                        type = TypeName.ULongLong;
                }
                else if (!isUnsigned && suffix.Length == 1)
                {
                    if (value <= int.MaxValue)
                        type = TypeName.Long;
                    else if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                    {
                        SyntaxErrorException e = new SyntaxErrorException(SR.IntegerConstantValueTooHugeToFitIntoInt64);
                        e.AddLocation(TokenContext);
                        throw e;
                    }
                }
                else if (isUnsigned && suffix.Length == 1)
                {
                    if (value <= uint.MaxValue)
                        type = TypeName.ULong;
                    else
                        type = TypeName.ULongLong;
                }
                else if (!isUnsigned)
                {
                    if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                    {
                        SyntaxErrorException e = new SyntaxErrorException(SR.IntegerConstantValueTooHugeToFitIntoInt64);
                        e.AddLocation(TokenContext);
                        throw e;
                    }
                }
                else
                {
                    type = TypeName.ULongLong;
                }
            }
            else
            {
                if (!hasSuffix)
                {
                    if (value <= int.MaxValue)
                        type = TypeName.Int;
                    else if (value <= uint.MaxValue)
                        type = TypeName.UInt;
                    else if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                        type = TypeName.ULongLong;
                }
                else if (isUnsigned && suffix.Length == 0)
                {
                    if (value <= uint.MaxValue)
                        type = TypeName.UInt;
                    else
                        type = TypeName.ULongLong;
                }
                else if (!isUnsigned && suffix.Length == 1)
                {
                    if (value <= int.MaxValue)
                        type = TypeName.Long;
                    else if (value <= uint.MaxValue)
                        type = TypeName.ULong;
                    else if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                        type = TypeName.ULongLong;
                }
                else if (isUnsigned && suffix.Length == 1)
                {
                    if (value <= uint.MaxValue)
                        type = TypeName.ULong;
                    else
                        type = TypeName.ULongLong;
                }
                else if (!isUnsigned)
                {
                    if (value <= long.MaxValue)
                        type = TypeName.LongLong;
                    else
                        type = TypeName.ULongLong;
                }
                else
                {
                    type = TypeName.ULongLong;
                }
            }
            TokenValue = IntegerConstant.New(value, type, TokenContext);
        }

        private void ScanFloatingValue(int radix)
        {
            string str = TokenContext.GetSource();
            TypeName type;
            if (str.EndsWith("f") || str.EndsWith("F"))
            {
                type = TypeName.Float;
                str = str.Substring(0, str.Length - 1);
            }
            else if (str.EndsWith("l") || str.EndsWith("L"))
            {
                type = TypeName.LongDouble;
                str = str.Substring(0, str.Length - 1);
            }
            else
            {
                type = TypeName.Double;
            }
            double value;
            if (radix == 10)
            {
                if (!double.TryParse(str, out value))
                    value = double.PositiveInfinity;
            }
            else
            {
                int index = str.IndexOfAny(new[] { 'p', 'P' });
                double fraction = 0.0;
                int i;
                for (i = 2; i < index; i++)
                {
                    if (str[i] == '.')
                    {
                        i++;
                        break;
                    }
                    int d = str[i].ValueOfDigit();
                    fraction = fraction * 16 + d;
                }
                double power = 1.0 / 16.0;
                for (; i < index; i++)
                {
                    int d = str[i].ValueOfDigit();
                    fraction += d * power;
                    power /= 16.0;
                }
                double exponent;
                string exp = str.Substring(index + 1);
                if (double.TryParse(exp, out exponent))
                    value = fraction * Math.Pow(2.0, exponent);
                else if (exp.StartsWith("-"))
                    value = (fraction == 0.0) ? 0.0 : double.Epsilon;
                else
                    value = (fraction == 0.0) ? 0.0 : double.PositiveInfinity;
            }
            TokenValue = FloatingConstant.New(value, type, TokenContext);
        }
        #endregion
    }

    partial class SyntaxTreeNodeWriter : ICharacterConstantFormatter, IStringLiteralFormatter
    {
        #region token writer
        public virtual bool IsKeyword(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            switch (name)
            {
                case "auto":
                case "break":
                case "case":
                case "char":
                case "const":
                case "continue":
                case "default":
                case "do":
                case "double":
                case "else":
                case "enum":
                case "extern":
                case "float":
                case "for":
                case "goto":
                case "if":
                case "inline":
                case "int":
                case "long":
                case "register":
                case "restrict":
                case "return":
                case "short":
                case "signed":
                case "sizeof":
                case "static":
                case "struct":
                case "switch":
                case "typedef":
                case "union":
                case "unsigned":
                case "void":
                case "volatile":
                case "while":
                case "_Alignas":
                case "_Alignof":
                case "_Atomic":
                case "_Bool":
                case "_Complex":
                case "_Generic":
                case "_Imaginary":
                case "_Noreturn":
                case "_Static_assert":
                case "_Thread_local":
                    return true;
                default:
                    return false;
            }
        }

        public virtual string EscapeKeyword(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (name.StartsWith("_"))
                return "\\u005F" + name.Substring(1);
            string prefix = name.Substring(0, name.Length - 1);
            char c = name[name.Length - 1];
            return string.Format("{0}\\u{1:X4}", prefix, (int)c);
        }

        public virtual bool IsIdentifierLetter(char c)
        {
            return c == '_' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
        }

        public virtual bool IsIdentifierLetterOrDigit(char c)
        {
            return c == '_' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9';
        }

        public virtual bool IsSimpleIdentifier(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (!IsIdentifierLetter(name[0]))
                return false;
            for (int i = 1; i < name.Length; i++)
                if (!IsIdentifierLetterOrDigit(name[i]))
                    return false;
            return true;
        }

        public virtual string EscapeIdentifier(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            StringBuilder sb = new StringBuilder();
            int index = 0;
            foreach (int c in name.EnumCodepoints())
            {
                if (index == 0 && IsIdentifierLetter((char)c))
                    sb.Append((char)c);
                else if (index > 0 && IsIdentifierLetterOrDigit((char)c))
                    sb.Append((char)c);
                else if (0 <= c && c <= 0xFFFF)
                    sb.AppendFormat("\\u{0:X4}", c);
                else
                    sb.AppendFormat("\\U{0:X8}", c);
                index++;
            }
            return sb.ToString();
        }

        public virtual string FormatIntegerConstant(IntegerConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.Type == TypeName.ULongLong)
                return n.Value.ToString(CultureInfo.InvariantCulture) + "ULL";
            else if (n.Type == TypeName.LongLong)
                return n.Value.ToString(CultureInfo.InvariantCulture) + "LL";
            else if (n.Type == TypeName.ULong)
                return n.Value.ToString(CultureInfo.InvariantCulture) + "UL";
            else if (n.Type == TypeName.Long)
                return n.Value.ToString(CultureInfo.InvariantCulture) + "L";
            else if (n.Type == TypeName.UInt)
                return n.Value.ToString(CultureInfo.InvariantCulture) + "U";
            else if (n.Type == TypeName.Int)
                return n.Value.ToString(CultureInfo.InvariantCulture);
            else
            {
                string type = n.Type.ToString();
                if (type == TypeName.ULongLong.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture) + "ULL";
                else if (type == TypeName.LongLong.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture) + "LL";
                else if (type == TypeName.ULong.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture) + "UL";
                else if (type == TypeName.Long.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture) + "L";
                else if (type == TypeName.UInt.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture) + "U";
                else if (type == TypeName.Int.ToString())
                    return n.Value.ToString(CultureInfo.InvariantCulture);
                else
                {
                    string message = string.Format(SR.IntegerConstantTypeInvalid, type.AsSingleQuote());
                    TypeErrorException e = new TypeErrorException(message);
                    e.AddLocation(n.Context);
                    e.AddLocation(n.Type.Context);
                    throw e;
                }
            }
        }

        public virtual string FormatFloatingConstant(FloatingConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            string suffix;
            if (n.Type == TypeName.LongDouble)
                suffix = "L";
            else if (n.Type == TypeName.Float)
                suffix = "F";
            else if (n.Type == TypeName.Double)
                suffix = "";
            else
            {
                string type = n.Type.ToString();
                if (type == TypeName.LongDouble.ToString())
                    suffix = "L";
                else if (type == TypeName.Float.ToString())
                    suffix = "F";
                else if (type == TypeName.Double.ToString())
                    suffix = "";
                else
                {
                    string message = string.Format(SR.FloatingConstantTypeInvalid, type.AsSingleQuote());
                    TypeErrorException e = new TypeErrorException(message);
                    e.AddLocation(n.Context);
                    e.AddLocation(n.Type.Context);
                    throw e;
                }
            }
            if (double.IsNaN(n.Value))
                return string.Format("(1e309{0} / 1e309{0})", suffix);
            else if (double.IsPositiveInfinity(n.Value))
                return string.Format("1e309{0}", suffix);
            else if (double.IsNegativeInfinity(n.Value))
                return string.Format("(-1e309{0})", suffix);
            else
            {
                string str = n.Value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
                str = str.Replace("e+", "e");
                if (str.IndexOfAny(new[] { '.', 'e' }) == -1)
                    str += ".0";
                str += suffix;
                return str;
            }
        }

        public virtual string EscapeCharacter(int c, char delimiter)
        {
            if (c < CharExtensions.MinChar || c > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            if (delimiter != '\'' && delimiter != '"')
                throw new ArgumentException(SR.DelimiterMustBeSingleOrDoubleQuote, nameof(delimiter));
            switch (c)
            {
                case '\a': return "\\a";
                case '\b': return "\\b";
                case '\t': return "\\t";
                case '\n': return "\\n";
                case '\v': return "\\v";
                case '\f': return "\\f";
                case '\r': return "\\r";
                case '\\': return "\\\\";
                case '\'': return delimiter == '\'' ? "\\'" : "'";
                case '"': return delimiter == '"' ? "\\\"" : "\"";
                default:
                    if (' ' <= c && c <= 0x7E)
                        return "" + (char)c;
                    else if (0 <= c && c <= 0xFF)
                        return string.Format("\\{0}{1}{2}", c / 64, c / 8 % 8, c % 8);
                    else if (0 <= c && c <= 0xFFFF)
                        return string.Format("\\u{0:X4}", c);
                    else
                        return string.Format("\\U{0:X8}", c);
            }
        }

        public virtual string FormatCharacterConstant(CharacterConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return n.AcceptCharacterConstantFormatter(this);
        }

        public virtual string FormatCharCharacterConstant(CharCharacterConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '\'';
            string str = EscapeCharacter(n.Value, delimiter);
            StringBuilder sb = new StringBuilder();
            sb.Append(delimiter);
            sb.Append(str);
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatChar16CharacterConstant(Char16CharacterConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '\'';
            string str = EscapeCharacter(n.Value, delimiter);
            StringBuilder sb = new StringBuilder();
            sb.Append("u");
            sb.Append(delimiter);
            sb.Append(str);
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatChar32CharacterConstant(Char32CharacterConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '\'';
            string str = EscapeCharacter(n.Value, delimiter);
            StringBuilder sb = new StringBuilder();
            sb.Append("U");
            sb.Append(delimiter);
            sb.Append(str);
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatWCharCharacterConstant(WCharCharacterConstant n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '\'';
            string str = EscapeCharacter(n.Value, delimiter);
            StringBuilder sb = new StringBuilder();
            sb.Append("L");
            sb.Append(delimiter);
            sb.Append(str);
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatStringLiteral(StringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            return n.AcceptStringLiteralFormatter(this);
        }

        public virtual string FormatCharStringLiteral(CharStringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '"';
            StringBuilder sb = new StringBuilder();
            sb.Append(delimiter);
            ReadOnlyCollection<byte> value = n.Value;
            for (int i = 0; i < value.Count - 1; i++)
            {
                string str = EscapeCharacter(value[i], delimiter);
                sb.Append(str);
            }
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatChar8StringLiteral(Char8StringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '"';
            StringBuilder sb = new StringBuilder();
            sb.Append("u8");
            sb.Append(delimiter);
            byte[] array = n.Value.ToArray();
            string chars = Encoding.UTF8.GetString(array, 0, array.Length - 1);
            foreach (int c in chars.EnumCodepoints())
            {
                string str = EscapeCharacter(c, delimiter);
                sb.Append(str);
            }
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatChar16StringLiteral(Char16StringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '"';
            StringBuilder sb = new StringBuilder();
            sb.Append("u");
            sb.Append(delimiter);
            char[] array = n.Value.ToArray();
            string chars = new string(array, 0, array.Length - 1);
            foreach (int c in chars.EnumCodepoints())
            {
                string str = EscapeCharacter(c, delimiter);
                sb.Append(str);
            }
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatChar32StringLiteral(Char32StringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '"';
            StringBuilder sb = new StringBuilder();
            sb.Append("U");
            sb.Append(delimiter);
            int[] array = n.Value.ToArray();
            for (int i = 0; i < array.Length - 1; i++)
            {
                string str = EscapeCharacter(array[i], delimiter);
                sb.Append(str);
            }
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual string FormatWCharStringLiteral(WCharStringLiteral n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            const char delimiter = '"';
            StringBuilder sb = new StringBuilder();
            sb.Append("L");
            sb.Append(delimiter);
            char[] array = n.Value.ToArray();
            string chars = new string(array, 0, array.Length - 1);
            foreach (int c in chars.EnumCodepoints())
            {
                string str = EscapeCharacter(c, delimiter);
                sb.Append(str);
            }
            sb.Append(delimiter);
            return sb.ToString();
        }

        public virtual void WriteIdentifier(Identifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (IsKeyword(n.Name))
            {
                string name = EscapeKeyword(n.Name);
                writer.Write(name);
            }
            else if (IsSimpleIdentifier(n.Name))
            {
                writer.Write(n.Name);
            }
            else
            {
                string name = EscapeIdentifier(n.Name);
                writer.Write(name);
            }
        }

        public virtual void WriteIntegerConstant(IntegerConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatIntegerConstant(n);
            writer.Write(str);
        }

        public virtual void WriteFloatingConstant(FloatingConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatFloatingConstant(n);
            writer.Write(str);
        }

        public virtual void WriteCharCharacterConstant(CharCharacterConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatCharCharacterConstant(n);
            writer.Write(str);
        }

        public virtual void WriteChar16CharacterConstant(Char16CharacterConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatChar16CharacterConstant(n);
            writer.Write(str);
        }

        public virtual void WriteChar32CharacterConstant(Char32CharacterConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatChar32CharacterConstant(n);
            writer.Write(str);
        }

        public virtual void WriteWCharCharacterConstant(WCharCharacterConstant n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatWCharCharacterConstant(n);
            writer.Write(str);
        }

        public virtual void WriteCharStringLiteral(CharStringLiteral n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatCharStringLiteral(n);
            writer.Write(str);
        }

        public virtual void WriteChar8StringLiteral(Char8StringLiteral n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatChar8StringLiteral(n);
            writer.Write(str);
        }

        public virtual void WriteChar16StringLiteral(Char16StringLiteral n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatChar16StringLiteral(n);
            writer.Write(str);
        }

        public virtual void WriteChar32StringLiteral(Char32StringLiteral n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatChar32StringLiteral(n);
            writer.Write(str);
        }

        public virtual void WriteWCharStringLiteral(WCharStringLiteral n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string str = FormatWCharStringLiteral(n);
            writer.Write(str);
        }
        #endregion

        #region expression writer
        public const int PrimaryExpressionOpPrecedence = 16;
        public const int PostfixExpressionOpPrecedence = 15;
        public const int UnaryExpressionOpPrecedence = 14;
        public const int CastExpressionOpPrecedence = 13;
        public const int MultiplicativeExpressionOpPrecedence = 12;
        public const int AdditiveExpressionOpPrecedence = 11;
        public const int ShiftExpressionOpPrecedence = 10;
        public const int RelationalExpressionOpPrecedence = 9;
        public const int EqualityExpressionOpPrecedence = 8;
        public const int BitwiseAndExpressionOpPrecedence = 7;
        public const int BitwiseXorExpressionOpPrecedence = 6;
        public const int BitwiseOrExpressionOpPrecedence = 5;
        public const int LogicalAndExpressionOpPrecedence = 4;
        public const int LogicalOrExpressionOpPrecedence = 3;
        public const int ConditionalExpressionOpPrecedence = 2;
        public const int AssignmentExpressionOpPrecedence = 1;
        public const int CommaExpressionOpPrecedence = 0;

        public virtual void WriteArgumentList(ArgumentList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteExpression(n.Items[i], writer, AssignmentExpressionOpPrecedence);
            }
        }

        public virtual void WriteNameReferenceExpression(NameReferenceExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteIdentifier(n.Name, writer);
        }

        public virtual void WriteIntegerConstantExpression(IntegerConstantExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteIntegerConstant(n.Value, writer);
        }

        public virtual void WriteFloatingConstantExpression(FloatingConstantExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteFloatingConstant(n.Value, writer);
        }

        public virtual void WriteCharacterConstantExpression(CharacterConstantExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteCharacterConstant(n.Value, writer);
        }

        public virtual void WriteStringLiteralExpression(StringLiteralExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStringLiteral(n.Value, writer);
        }

        public virtual void WriteGroupExpression(GroupExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("(");
            WriteExpression(n.Operand, writer);
            writer.Write(")");
        }

        public virtual void WriteSubscriptExpression(SubscriptExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            writer.Write("[");
            WriteExpression(n.Index, writer);
            writer.Write("]");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteCallExpression(CallExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            writer.Write("(");
            WriteArgumentList(n.Arguments, writer);
            writer.Write(")");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteDotAccessExpression(DotAccessExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            writer.Write(".");
            WriteIdentifier(n.Member, writer);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteArrowAccessExpression(ArrowAccessExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            writer.Write("->");
            WriteIdentifier(n.Member, writer);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WritePostfixIncrementExpression(PostfixIncrementExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            string str = n.Operand.ToString(this);
            if (str.EndsWith("+"))
                writer.Write(" ");
            writer.Write("++");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WritePostfixDecrementExpression(PostfixDecrementExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = PostfixExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Operand, writer, prec);
            string str = n.Operand.ToString(this);
            if (str.EndsWith("-"))
                writer.Write(" ");
            writer.Write("--");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WritePrefixIncrementExpression(PrefixIncrementExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("++");
            string str = n.Operand.ToString(this);
            if (str.StartsWith("+"))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WritePrefixDecrementExpression(PrefixDecrementExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("--");
            string str = n.Operand.ToString(this);
            if (str.StartsWith("-"))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteAddressOfExpression(AddressOfExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("&");
            string str = n.Operand.ToString(this);
            if (str.StartsWith("&"))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteDereferenceExpression(DereferenceExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("*");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WritePositiveExpression(PositiveExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("+");
            string str = n.Operand.ToString(this);
            if (str.StartsWith("+"))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteNegativeExpression(NegativeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("-");
            string str = n.Operand.ToString(this);
            if (str.StartsWith("-"))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseNotExpression(BitwiseNotExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("~");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLogicalNotExpression(LogicalNotExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("!");
            WriteExpression(n.Operand, writer, CastExpressionOpPrecedence);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteSizeofValueExpression(SizeofValueExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("sizeof");
            string str = n.Operand.ToString(this, prec);
            if (!str.StartsWith("("))
                writer.Write(" ");
            WriteExpression(n.Operand, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteSizeofExpression(SizeofExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("sizeof(");
            WriteTypeName(n.Type, writer);
            writer.Write(")");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteAlignofExpression(AlignofExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = UnaryExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("_Alignof(");
            WriteTypeName(n.Type, writer);
            writer.Write(")");
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteCastExpression(CastExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = CastExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            writer.Write("(");
            WriteTypeName(n.Type, writer);
            writer.Write(")");
            WriteExpression(n.Operand, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteMultiplyExpression(MultiplyExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = MultiplicativeExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" * ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteDivideExpression(DivideExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = MultiplicativeExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" / ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteModuloExpression(ModuloExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = MultiplicativeExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" % ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteAddExpression(AddExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AdditiveExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" + ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteSubtractExpression(SubtractExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AdditiveExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" - ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLeftShiftExpression(LeftShiftExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = ShiftExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" << ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteRightShiftExpression(RightShiftExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = ShiftExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" >> ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLessThanExpression(LessThanExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = RelationalExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" < ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteGreaterThanExpression(GreaterThanExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = RelationalExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" > ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLessEqualExpression(LessEqualExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = RelationalExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" <= ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteGreaterEqualExpression(GreaterEqualExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = RelationalExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" >= ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteEqualToExpression(EqualToExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = EqualityExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" == ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteNotEqualToExpression(NotEqualToExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = EqualityExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" != ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseAndExpression(BitwiseAndExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = BitwiseAndExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" & ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseXorExpression(BitwiseXorExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = BitwiseXorExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" ^ ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseOrExpression(BitwiseOrExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = BitwiseOrExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" | ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLogicalAndExpression(LogicalAndExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = LogicalAndExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" && ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLogicalOrExpression(LogicalOrExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = LogicalOrExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(" || ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteConditionalExpression(ConditionalExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = ConditionalExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Condition, writer, prec + 1);
            writer.Write(" ? ");
            WriteExpression(n.TruePart, writer);
            writer.Write(" : ");
            WriteExpression(n.FalsePart, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteAssignExpression(AssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" = ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteMultiplyAssignExpression(MultiplyAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" *= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteDivideAssignExpression(DivideAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" /= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteModuloAssignExpression(ModuloAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" %= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteAddAssignExpression(AddAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" += ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteSubtractAssignExpression(SubtractAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" -= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteLeftShiftAssignExpression(LeftShiftAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" <<= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteRightShiftAssignExpression(RightShiftAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" >>= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseAndAssignExpression(BitwiseAndAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" &= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseXorAssignExpression(BitwiseXorAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" ^= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteBitwiseOrAssignExpression(BitwiseOrAssignExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = AssignmentExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, UnaryExpressionOpPrecedence);
            writer.Write(" |= ");
            WriteExpression(n.Right, writer, prec);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteCommaExpression(CommaExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            const int prec = CommaExpressionOpPrecedence;
            if (d > prec)
                writer.Write("(");
            WriteExpression(n.Left, writer, prec);
            writer.Write(", ");
            WriteExpression(n.Right, writer, prec + 1);
            if (d > prec)
                writer.Write(")");
        }

        public virtual void WriteConstantExpression(ConstantExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Operand, writer, ConditionalExpressionOpPrecedence);
        }

        public virtual void WriteArgumentExpression(ArgumentExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Operand, writer, AssignmentExpressionOpPrecedence);
        }
        #endregion

        #region declaration writer
        public virtual void WriteDataDeclaration(DataDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarationSpecifierList(n.Specifiers, writer);
            if (n.Declarators != null)
            {
                writer.Write(" ");
                WriteInitDeclaratorList(n.Declarators, writer);
            }
            writer.Write(";");
        }

        public virtual void WriteStaticAssertionDeclaration(StaticAssertionDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStaticAssertion(n.Declaration, writer);
        }

        public virtual void WriteStorageClassSpecifierDeclarationSpecifier(StorageClassSpecifierDeclarationSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStorageClassSpecifier(n.Specifier, writer);
        }

        public virtual void WriteTypeSpecifierDeclarationSpecifier(TypeSpecifierDeclarationSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeSpecifier(n.Specifier, writer);
        }

        public virtual void WriteTypeQualifierDeclarationSpecifier(TypeQualifierDeclarationSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeQualifier(n.Qualifier, writer);
        }

        public virtual void WriteFunctionSpecifierDeclarationSpecifier(FunctionSpecifierDeclarationSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteFunctionSpecifier(n.Specifier, writer);
        }

        public virtual void WriteAlignmentSpecifierDeclarationSpecifier(AlignmentSpecifierDeclarationSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteAlignmentSpecifier(n.Specifier, writer);
        }

        public virtual void WriteDeclarationSpecifierList(DeclarationSpecifierList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(" ");
                WriteDeclarationSpecifier(n.Items[i], writer);
            }
        }

        public virtual void WriteInitDeclarator(InitDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarator(n.Declarator, writer);
            if (n.Initializer != null)
            {
                writer.Write(" = ");
                WriteInitializer(n.Initializer, writer);
            }
        }

        public virtual void WriteInitDeclaratorList(InitDeclaratorList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteInitDeclarator(n.Items[i], writer);
            }
        }

        public virtual void WriteTypedefStorageClassSpecifier(TypedefStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("typedef");
        }

        public virtual void WriteExternStorageClassSpecifier(ExternStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("extern");
        }

        public virtual void WriteStaticStorageClassSpecifier(StaticStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("static");
        }

        public virtual void WriteThreadLocalStorageClassSpecifier(ThreadLocalStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Thread_local");
        }

        public virtual void WriteAutoStorageClassSpecifier(AutoStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("auto");
        }

        public virtual void WriteRegisterStorageClassSpecifier(RegisterStorageClassSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("register");
        }

        public virtual void WriteVoidTypeSpecifier(VoidTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("void");
        }

        public virtual void WriteCharTypeSpecifier(CharTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("char");
        }

        public virtual void WriteShortTypeSpecifier(ShortTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public virtual void WriteIntTypeSpecifier(IntTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public virtual void WriteLongTypeSpecifier(LongTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }

        public virtual void WriteChar8TTypeSpecifier(Char8TTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("__char8_t");
        }

        public virtual void WriteChar16TTypeSpecifier(Char16TTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("__char16_t");
        }

        public virtual void WriteChar32TTypeSpecifier(Char32TTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("__char32_t");
        }

        public virtual void WriteWCharTTypeSpecifier(WCharTTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("__wchar_t");
        }

        public virtual void WriteIntPtrTTypeSpecifier(IntPtrTTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("__intptr_t");
        }

        public virtual void WriteFloatTypeSpecifier(FloatTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public virtual void WriteDoubleTypeSpecifier(DoubleTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public virtual void WriteSignedTypeSpecifier(SignedTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("signed");
        }

        public virtual void WriteUnsignedTypeSpecifier(UnsignedTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("unsigned");
        }

        public virtual void WriteBoolTypeSpecifier(BoolTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Bool");
        }

        public virtual void WriteComplexTypeSpecifier(ComplexTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Complex");
        }

        public virtual void WriteAtomicTypeSpecifier(AtomicTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Atomic(");
            WriteTypeName(n.TypeName, writer);
            writer.Write(")");
        }

        public virtual void WriteStructTypeSpecifier(StructTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(n.IsUnion ? "union" : "struct");
            if (n.TagName != null)
            {
                writer.Write(" ");
                WriteIdentifier(n.TagName, writer);
            }
            writer.Write("\n");
            writer.Write("{");
            writer.Write("\n");
            writer.OpenBlock();
            WriteStructDeclarationList(n.Declarations, writer);
            writer.CloseBlock();
            writer.Write("}");
        }

        public virtual void WritePartialStructTypeSpecifier(PartialStructTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(n.IsUnion ? "union" : "struct");
            writer.Write(" ");
            WriteIdentifier(n.TagName, writer);
        }

        public virtual void WriteEnumTypeSpecifier(EnumTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("enum");
            if (n.TagName != null)
            {
                writer.Write(" ");
                WriteIdentifier(n.TagName, writer);
            }
            writer.Write("\n");
            writer.Write("{");
            writer.OpenBlock();
            writer.Write("\n");
            WriteEnumeratorList(n.Enumerators, writer);
            if (n.HasTrailingComma)
                writer.Write(",");
            writer.Write("\n");
            writer.CloseBlock();
            writer.Write("}");
        }

        public virtual void WritePartialEnumTypeSpecifier(PartialEnumTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("enum ");
            WriteIdentifier(n.TagName, writer);
        }

        public virtual void WriteTypedefNameTypeSpecifier(TypedefNameTypeSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypedefName(n.Name, writer);
        }

        public virtual void WriteFieldDeclarationStructDeclaration(FieldDeclarationStructDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteSpecifierQualifierList(n.SpecifierQualifiers, writer);
            if (n.Declarators != null)
            {
                writer.Write(" ");
                WriteStructDeclaratorList(n.Declarators, writer);
            }
            writer.Write(";");
        }

        public virtual void WriteStaticAssertionStructDeclaration(StaticAssertionStructDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStaticAssertion(n.Declaration, writer);
        }

        public virtual void WriteStructDeclarationList(StructDeclarationList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                WriteStructDeclaration(n.Items[i], writer);
                writer.Write("\n");
            }
        }

        public virtual void WriteTypeSpecifierSpecifierQualifierListItem(TypeSpecifierSpecifierQualifierListItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeSpecifier(n.Specifier, writer);
        }

        public virtual void WriteTypeQualifierSpecifierQualifierListItem(TypeQualifierSpecifierQualifierListItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeQualifier(n.Qualifier, writer);
        }

        public virtual void WriteAlignmentSpecifierSpecifierQualifierListItem(AlignmentSpecifierSpecifierQualifierListItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteAlignmentSpecifier(n.Specifier, writer);
        }

        public virtual void WriteSpecifierQualifierList(SpecifierQualifierList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(" ");
                WriteSpecifierQualifierListItem(n.Items[i], writer);
            }
        }

        public virtual void WriteFieldStructDeclarator(FieldStructDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarator(n.Declarator, writer);
        }

        public virtual void WriteBitFieldStructDeclarator(BitFieldStructDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Declarator != null)
            {
                WriteDeclarator(n.Declarator, writer);
                writer.Write(" ");
            }
            writer.Write(": ");
            WriteExpression(n.Bits, writer);
        }

        public virtual void WriteStructDeclaratorList(StructDeclaratorList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteStructDeclarator(n.Items[i], writer);
            }
        }

        public virtual void WriteEnumerator(Enumerator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteIdentifier(n.Name, writer);
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
            }
        }

        public virtual void WriteEnumeratorList(EnumeratorList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(",\n");
                WriteEnumerator(n.Items[i], writer);
            }
        }

        public virtual void WriteConstTypeQualifier(ConstTypeQualifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("const");
        }

        public virtual void WriteRestrictTypeQualifier(RestrictTypeQualifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("restrict");
        }

        public virtual void WriteVolatileTypeQualifier(VolatileTypeQualifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("volatile");
        }

        public virtual void WriteAtomicTypeQualifier(AtomicTypeQualifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Atomic");
        }

        public virtual void WriteInlineFunctionSpecifier(InlineFunctionSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("inline");
        }

        public virtual void WriteNoreturnFunctionSpecifier(NoreturnFunctionSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Noreturn");
        }

        public virtual void WriteAlignasAlignmentSpecifier(AlignasAlignmentSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Alignas(");
            WriteTypeName(n.TypeName, writer);
            writer.Write(")");
        }

        public virtual void WriteAlignasValueAlignmentSpecifier(AlignasValueAlignmentSpecifier n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Alignas(");
            WriteExpression(n.Value, writer);
            writer.Write(")");
        }

        public virtual void WriteDeclarator(Declarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Pointer != null)
            {
                WritePointerDeclarator(n.Pointer, writer);
                string str = n.Pointer.ToString(this);
                if (!str.EndsWith("*"))
                    writer.Write(" ");
            }
            WriteDirectDeclarator(n.Direct, writer);
        }

        public virtual void WriteNameDirectDeclarator(NameDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteIdentifier(n.Name, writer);
        }

        public virtual void WriteGroupDirectDeclarator(GroupDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("(");
            WriteDeclarator(n.Declarator, writer);
            writer.Write(")");
        }

        public virtual void WriteArrayDirectDeclarator(ArrayDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("[");
            if (n.Qualifiers != null)
                WriteTypeQualifierList(n.Qualifiers, writer);
            if (n.Size != null)
            {
                if (n.Qualifiers != null)
                    writer.Write(" ");
                WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            }
            writer.Write("]");
        }

        public virtual void WritePrefixStaticArrayDirectDeclarator(PrefixStaticArrayDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("[");
            writer.Write("static");
            if (n.Qualifiers != null)
            {
                writer.Write(" ");
                WriteTypeQualifierList(n.Qualifiers, writer);
            }
            writer.Write(" ");
            WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            writer.Write("]");
        }

        public virtual void WritePostfixStaticArrayDirectDeclarator(PostfixStaticArrayDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("[");
            WriteTypeQualifierList(n.Qualifiers, writer);
            writer.Write(" static ");
            WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            writer.Write("]");
        }

        public virtual void WriteStarArrayDirectDeclarator(StarArrayDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("[");
            if (n.Qualifiers != null)
            {
                WriteTypeQualifierList(n.Qualifiers, writer);
                writer.Write(" ");
            }
            writer.Write("*");
            writer.Write("]");
        }

        public virtual void WriteFunctionDirectDeclarator(FunctionDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("(");
            WriteParameterDeclarationList(n.Parameters, writer);
            writer.Write(")");
        }

        public virtual void WriteOldStyleFunctionDirectDeclarator(OldStyleFunctionDirectDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectDeclarator(n.Declarator, writer);
            writer.Write("(");
            for (int i = 0; i < n.Parameters.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteIdentifier(n.Parameters[i], writer);
            }
            writer.Write(")");
        }

        public virtual void WritePointerDeclarator(PointerDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0 && n.Items[i - 1].Qualifiers != null)
                    writer.Write(" ");
                WritePointerDeclaratorItem(n.Items[i], writer);
            }
        }

        public virtual void WritePointerDeclaratorItem(PointerDeclaratorItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("*");
            if (n.Qualifiers != null)
                WriteTypeQualifierList(n.Qualifiers, writer);
        }

        public virtual void WriteTypeQualifierList(TypeQualifierList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(" ");
                WriteTypeQualifier(n.Items[i], writer);
            }
        }

        public virtual void WriteParameterDeclarationList(ParameterDeclarationList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteParameterDeclaration(n.Items[i], writer);
            }
            if (n.HasTrailingEllipsis)
            {
                writer.Write(", ");
                writer.Write("...");
            }
        }

        public virtual void WriteConcreteParameterDeclaration(ConcreteParameterDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarationSpecifierList(n.Specifiers, writer);
            writer.Write(" ");
            WriteDeclarator(n.Declarator, writer);
        }

        public virtual void WriteAbstractParameterDeclaration(AbstractParameterDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarationSpecifierList(n.Specifiers, writer);
            if (n.Declarator != null)
            {
                writer.Write(" ");
                WriteAbstractDeclarator(n.Declarator, writer);
            }
        }

        public virtual void WriteTypeName(TypeName n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteSpecifierQualifierList(n.SpecifierQualifiers, writer);
            if (n.Declarator != null)
            {
                writer.Write(" ");
                WriteAbstractDeclarator(n.Declarator, writer);
            }
        }

        public virtual void WritePointerAbstractDeclarator(PointerAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WritePointerDeclarator(n.Declarator, writer);
        }

        public virtual void WritePointerDirectAbstractDeclarator(PointerDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Declarator != null)
                WritePointerDeclarator(n.Declarator, writer);
            WriteDirectAbstractDeclarator(n.Direct, writer);
        }

        public virtual void WriteGroupDirectAbstractDeclarator(GroupDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("(");
            WriteAbstractDeclarator(n.Declarator, writer);
            writer.Write(")");
        }

        public virtual void WriteArrayDirectAbstractDeclarator(ArrayDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("[");
            if (n.Qualifiers != null)
                WriteTypeQualifierList(n.Qualifiers, writer);
            if (n.Size != null)
            {
                if (n.Qualifiers != null)
                    writer.Write(" ");
                WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            }
            writer.Write("]");
        }

        public virtual void WritePrefixStaticArrayDirectAbstractDeclarator(PrefixStaticArrayDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("[");
            writer.Write("static");
            if (n.Qualifiers != null)
            {
                writer.Write(" ");
                WriteTypeQualifierList(n.Qualifiers, writer);
            }
            writer.Write(" ");
            WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            writer.Write("]");
        }

        public virtual void WritePostfixStaticArrayDirectAbstractDeclarator(PostfixStaticArrayDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("[");
            WriteTypeQualifierList(n.Qualifiers, writer);
            writer.Write(" static ");
            WriteExpression(n.Size, writer, AssignmentExpressionOpPrecedence);
            writer.Write("]");
        }

        public virtual void WriteStarArrayDirectAbstractDeclarator(StarArrayDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("[*]");
        }

        public virtual void WriteFunctionDirectAbstractDeclarator(FunctionDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("(");
            WriteParameterDeclarationList(n.Parameters, writer);
            writer.Write(")");
        }

        public virtual void WriteOldStyleFunctionDirectAbstractDeclarator(OldStyleFunctionDirectAbstractDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDirectAbstractDeclarator(n.Declarator, writer);
            writer.Write("()");
        }

        public virtual void WriteTypedefName(TypedefName n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteIdentifier(n.Name, writer);
        }

        public virtual void WriteSingleInitializer(SingleInitializer n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Value, writer);
        }

        public virtual void WriteMultipleInitializer(MultipleInitializer n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("\n");
            writer.Write("{");
            writer.OpenBlock();
            writer.Write("\n");
            WriteInitializerList(n.Initializers, writer);
            if (n.HasTrailingComma)
                writer.Write(",");
            writer.Write("\n");
            writer.CloseBlock();
            writer.Write("}");
        }

        public virtual void WriteInitializerListItem(InitializerListItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Designation != null)
                WriteDesignation(n.Designation, writer);
            WriteInitializer(n.Value, writer);
        }

        public virtual void WriteInitializerList(InitializerList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                    writer.Write(",\n");
                WriteInitializerListItem(n.Items[i], writer);
            }
        }

        public virtual void WriteDesignation(Designation n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDesignatorList(n.Designators, writer);
            writer.Write(" = ");
        }

        public virtual void WriteDesignatorList(DesignatorList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
                WriteDesignator(n.Items[i], writer);
        }

        public virtual void WriteSubscriptDesignator(SubscriptDesignator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("[");
            WriteExpression(n.Index, writer);
            writer.Write("]");
        }

        public virtual void WriteFieldDesignator(FieldDesignator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
        }

        public virtual void WriteStaticAssertion(StaticAssertion n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("_Static_assert(");
            WriteExpression(n.Value, writer, AssignmentExpressionOpPrecedence);
            writer.Write(", ");
            WriteExpression(n.Message, writer, PrimaryExpressionOpPrecedence);
            writer.Write(");");
        }
        #endregion

        #region statement writer
        public virtual void WriteLabelStatement(LabelStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.CloseBlock();
            WriteIdentifier(n.Label, writer);
            writer.Write(":\n");
            writer.OpenBlock();
            WriteStatement(n.Operand, writer);
        }

        public virtual void WriteCaseStatement(CaseStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.CloseBlock();
            writer.Write("case ");
            WriteExpression(n.Value, writer);
            writer.Write(":\n");
            writer.OpenBlock();
            WriteStatement(n.Operand, writer);
        }

        public virtual void WriteDefaultStatement(DefaultStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.CloseBlock();
            writer.Write("default:\n");
            writer.OpenBlock();
            WriteStatement(n.Operand, writer);
        }

        public virtual void WriteCompoundStatement(CompoundStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.OpenCurlyBlock();
            if (n.Operands != null)
                WriteBlockItemList(n.Operands, writer);
            writer.CloseCurlyBlock();
        }

        public virtual void WriteExpressionStatement(ExpressionStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public virtual void WriteNullStatement(NullStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(";\n");
        }

        public virtual void WriteIfStatement(IfStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("if (");
            WriteExpression(n.Test, writer);
            writer.Write(")\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
            if (n.OrElse != null)
            {
                writer.Write("else\n");
                if (!n.OrElse.IsCompound)
                    writer.OpenBlock();
                WriteStatement(n.OrElse, writer);
                if (!n.OrElse.IsCompound)
                    writer.CloseBlock();
            }
        }

        public virtual void WriteSwitchStatement(SwitchStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("switch (");
            WriteExpression(n.Test, writer);
            writer.Write(")\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
        }

        public virtual void WriteWhileStatement(WhileStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("while (");
            WriteExpression(n.Test, writer);
            writer.Write(")\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
        }

        public virtual void WriteDoWhileStatement(DoWhileStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("do\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
            writer.Write("while (");
            WriteExpression(n.Test, writer);
            writer.Write(");\n");
        }

        public virtual void WriteForStatement(ForStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("for (");
            if (n.Init == null && n.Test == null && n.Step == null)
                writer.Write(";;");
            else
            {
                if (n.Init != null)
                    WriteExpression(n.Init, writer);
                writer.Write("; ");
                if (n.Test != null)
                    WriteExpression(n.Test, writer);
                writer.Write("; ");
                if (n.Step != null)
                    WriteExpression(n.Step, writer);
            }
            writer.Write(")\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
        }

        public virtual void WriteForBindStatement(ForBindStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("for (");
            if (n.Bind == null && n.Test == null && n.Step == null)
                writer.Write(";;");
            else
            {
                WriteDeclaration(n.Bind, writer);
                writer.Write(" ");
                if (n.Test != null)
                    WriteExpression(n.Test, writer);
                writer.Write("; ");
                if (n.Step != null)
                    WriteExpression(n.Step, writer);
            }
            writer.Write(")\n");
            if (!n.Body.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.Body.IsCompound)
                writer.CloseBlock();
        }

        public virtual void WriteGotoStatement(GotoStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("goto ");
            WriteIdentifier(n.Label, writer);
            writer.Write(";\n");
        }

        public virtual void WriteContinueStatement(ContinueStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("continue;\n");
        }

        public virtual void WriteBreakStatement(BreakStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("break;\n");
        }

        public virtual void WriteReturnStatement(ReturnStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("return");
            if (n.Value != null)
            {
                writer.Write(" ");
                WriteExpression(n.Value, writer);
            }
            writer.Write(";\n");
        }

        public virtual void WriteDeclarationBlockItem(DeclarationBlockItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclaration(n.Declaration, writer);
            writer.Write("\n");
        }

        public virtual void WriteStatementBlockItem(StatementBlockItem n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStatement(n.Statement, writer);
        }

        public virtual void WriteBlockItemList(BlockItemList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
                WriteBlockItem(n.Items[i], writer);
        }
        #endregion

        #region external definitions writer
        public virtual void WriteFunctionDefinition(FunctionDefinition n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclarationSpecifierList(n.Specifiers, writer);
            writer.Write(" ");
            WriteDeclarator(n.Declarator, writer);
            writer.Write("\n");
            if (n.Declarations != null)
                WriteDeclarationList(n.Declarations, writer);
            WriteStatement(n.Body, writer);
        }

        public virtual void WriteDeclarationList(DeclarationList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.OpenBlock();
            for (int i = 0; i < n.Items.Count; i++)
            {
                WriteDeclaration(n.Items[i], writer);
                writer.Write("\n");
            }
            writer.CloseBlock();
        }

        public virtual void WriteFunctionExternalDeclaration(FunctionExternalDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteFunctionDefinition(n.Function, writer);
        }

        public virtual void WriteDataExternalDeclaration(DataExternalDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDeclaration(n.Data, writer);
            writer.Write("\n");
        }

        public virtual void WriteExternalDeclarationList(ExternalDeclarationList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Items.Count; i++)
            {
                if (i > 0)
                {
                    if (n.Items[i].IsFunction || n.Items[i - 1].IsFunction)
                        writer.Write("\n");
                }
                WriteExternalDeclaration(n.Items[i], writer);
            }
        }

        public virtual void WriteTranslationUnit(TranslationUnit n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExternalDeclarationList(n.Declarations, writer);
        }
        #endregion
    }
}
